/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package technotheist.imgcog.features;

import technotheist.imgcog.base.Module;

/**
 * This project is the intellectual property and only to be used under license
 * guidelines. Please see the containing license.txt file for license
 * information and availability of use. The author takes no responsibility for
 * any damage or non-functionality caused by use of this software.
 *
 * For use of any content with a different license, please contact the author.
 *
 * @author Mitchel Strachan <technotheist@gmail.com>
 */
public abstract class MultiFilter extends Module {

    @Override
    protected void process() {
        throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
    }

    protected abstract class MultiFilterRunner extends Runner {

        static final byte LOCAL_COLOR_NORM = 0;
        static final byte CONVOLVE = 1;
        static final byte DIFF_OF_CONVOLVE = 2;
        protected int xLen, yLen,
                inputZLen;  //monochrome = 1, rgb = 3
        protected int outputZLen;
        protected int filterCount;
        @Constant
        protected byte[] filterType;
        @Constant
        protected int[] kernelANum;      //c = -1 if no kernel used
        @Constant
        protected int[] kernelBNum;      //c = -1 if no kernel used
        @Constant
        protected boolean[] preNormalized;
        @Constant
        protected int[] convolveKernelIndex0;   //
        @Constant
        protected int[] convRadiusX;   //sideLenX = radius * 2 + 1;
        @Constant
        protected int[] convRadiusY;   //sideLenY = radius * 2 + 1;
        protected int convolveKernelCount;
        @Constant
        protected float[] convolveKernels;

        protected MultiFilterRunner(MultiFilterConfig config) {
            super(config);
        }

        private void copyBack(int z0, int zLen) {
            int x = getGlobalId(0);
            int y = getGlobalId(1);
            int out0 = index3D(x, y, z0, yLen, outputZLen);
            int in0 = index3D(x, y, 0, yLen, inputZLen);
            for (int z = 0; z < zLen; z++) {
                layer0Nodes[in0 + z] = layerNOutput[out0 + z];
            }
        }

        private float getConvolve(int kernelNum, int inputX, int inputY, int inputZ) {
            float fSum = 0;
            float cSum = 0;
            int crx = convRadiusX[kernelNum];
            int cry = convRadiusY[kernelNum];
            int ci0 = convolveKernelIndex0[kernelNum];
            int cLenY = cry * 2 + 1;
            int ci = 0;
            for (int cx = -crx; cx <= crx; cx++) {
                for (int cy = -cry; cy <= cry; cy++) {
                    int x = inputX + cx;
                    int y = inputY + cy;
                    if (x >= 0 && y >= 0 && x < xLen && y < yLen) {
                        int inputIndex = index3D(x, y, inputZ, yLen, inputZLen);
                        int convIndex = ci0 + index2D(cx + crx, cy + cry, cLenY);
                        float c = convolveKernels[convIndex];
                        cSum += c;
                        fSum += layer0Nodes[inputIndex] * c;
                    }
                }
            }
            return fSum / cSum;
        }

        private float getConvolveAvg(int kernelNum, int inputX, int inputY) {
            float fSum = 0;
            float cSum = 0;
            int crx = convRadiusX[kernelNum];
            int cry = convRadiusY[kernelNum];
            int ci0 = convolveKernelIndex0[kernelNum];
            int cLenY = cry * 2 + 1;
            int ci = 0;
            for (int cx = -crx; cx <= crx; cx++) {
                for (int cy = -cry; cy <= cry; cy++) {
                    int x = inputX + cx;
                    int y = inputY + cy;
                    if (x >= 0 && y >= 0 && x < xLen && y < yLen) {
                        //int inputIndex = index3D(x, y, 0, yLen, inputZLen);
                        int convIndex = ci0 + index2D(cx + crx, cy + cry, cLenY);
                        float c = convolveKernels[convIndex];
                        cSum += c;
                        fSum += getAvgZ(x, y) * c;
                    }
                }
            }
            return fSum / cSum;
        }

        private float getAvgZ(int x, int y) {
            return getAvgZRange(x, y, 0, inputZLen);
        }

        private float getAvgZRange(int x, int y, int z0, int zLen) {
            float sum = 0;
            int i0 = index3D(x, y, z0, yLen, inputZLen);
            for (int z = 0; z < zLen; z++) {
                sum += layer0Nodes[i0 + z];
            }
            return sum / zLen;
        }

        private float getLocalNormUniform(int inputX, int inputY, int inputZ, int d) {
            float min = 9999999f;
            float max = -9999999f;
            float v = 0;
            for (int dx = -d; dx <= d; dx++) {
                for (int dy = -d; dy <= d; dy++) {
                    int x = inputX + dx;
                    int y = inputY + dy;
                    if (x >= 0 && y >= 0 && x < xLen && y < yLen) {
                        float val = layer0Nodes[index3D(x, y, inputZ, yLen, inputZLen)];
                        if (dx == 0 && dy == 0) {
                            v = val;
                        }
                        if (val < min) {
                            min = val;
                        }
                        if (val > max) {
                            max = val;
                        }
                    }
                }
            }
            return (v - min) / (max - min);
        }

        private float getLocalNormConvolve(int kernelNum, int inputX, int inputY, int inputZ) {
            float max = -9999999f;
            float min = 9999999f;
            int crx = convRadiusX[kernelNum];
            int cry = convRadiusY[kernelNum];
            int ci0 = convolveKernelIndex0[kernelNum];
            int cLenY = cry * 2 + 1;
            int ci = 0;
            float p = index3D(inputX, inputY, inputZ, yLen, inputZLen);
            for (int cx = -crx; cx <= crx; cx++) {
                for (int cy = -cry; cy <= cry; cy++) {
                    int x = inputX + cx;
                    int y = inputY + cy;
                    if (x >= 0 && y >= 0 && x < xLen && y < yLen) {
                        int inputIndex = index3D(x, y, inputZ, yLen, inputZLen);
                        int convIndex = ci0 + index2D(cx + crx, cy + cry, cLenY);
                        float val = layer0Nodes[index3D(x, y, inputZ, yLen, inputZLen)];
                        float c = convolveKernels[convIndex];
                        float v = c * val + (1 - c) * p;
                        if (v < min) {
                            min = v;
                        }
                        if (v > max) {
                            max = v;
                        }
                    }
                }
            }
            return (p - min) / (max - min);
        }
    }
}
